#include <stdio.h>  
#include <stdlib.h>  
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <http_download.h>
#include <common.h>
#include <system.h>

#define HTTP_DATA_BUF_SIZE    	(1024*512)
#define HTTP_HEAD_BUF_SIZE    	(1024)

#define HTTP_GET_FILE					\
	"GET %s HTTP/1.1\r\n"				\
	"Host:%s\r\n"						\
	"Range:bytes=%s\r\n"				\
	"Keep-Alive:200\r\n"				\
	"Connection:Keep-Alive\r\n\r\n"

static int http_send(int channel, void *buf, int len)
{
	if(channel > 0)
	{
		return write(channel, buf, len); 
	}

	return -1;
}

static int http_recv(int channel, void *buf, int len)
{
	if(channel > 0)
	{
		return read(channel, buf, len);
	}

	return -1;
}

//HTTP
static int http_get_body_length(char *revbuf)
{
	int len = 0;
	char *p1 = NULL;

	p1 = strstr(revbuf, "Content-Length");
	if(p1)
	{
		p1 = p1 + strlen("Content-Length") + 2;
		len = atoi(p1);
		return len;
	}

	return 0;
}

//读取http返回的协议实体主体长度
static int http_get_package_length(char *revbuf)
{
	char *ptr = NULL;
	int body = 0;
	int head = 0;

	body = http_get_body_length(revbuf);
	if(body <= 0)
	{
		return 0;
	}

	ptr = strstr(revbuf, "\r\n\r\n");
	if(ptr)
	{
		head = ptr + 4 - revbuf;		//4 是 \r\n\r\n 的长度
		return body + head;
	}

	return 0;
}

//从url提取文件名
int http_get_file_name(char *url, char *save_path, char *file_name)
{
	char *p = strrchr(url, '/');
	if(p)
	{
		if(save_path) {
			strcat(file_name, save_path);
			strcat(file_name, "/");
		}
		strcat(file_name, p + 1);
		return 0;
	}

	return -1;
}

//从url提取文件在服务器中的路径
static int http_get_path(char *url, char *path)
{
	char *p = NULL;
	int offset = 0;

	p = strstr(url,"http://");
	if(p)
	{
		offset = strlen("http://");
	}
	else
	{
		p = strstr(url, "https://");
		if(p)
		{
			offset = strlen("https://");
		}
	}

	p = strchr(url + offset,'/');
	if(p == NULL)
	{
		return -1;
	}
	else
	{
		strcpy(path, p);
		return 0;
	}
}

//从url提取ip/域名和端口
static int http_get_ip_port(char *url, char *ip, char *port)
{
	char *ptr = NULL;
	int offset = 0;
	char domain[128] = {0};

	ptr = strstr(url, "http://");
	if(ptr)
	{
		offset = strlen("http://");
	}
	else
	{
		ptr = strstr(url, "https://");
		if(ptr)
		{
			offset = strlen("https://");
		}
	}

	ptr = strchr(url + offset, '/');
	if(ptr == NULL)
	{
		DEBUG("url:%s format error", url);
		return -1;
	}
	else
	{
		memcpy(domain, url + offset, ptr - url - offset);
		ptr = strchr(domain, ':');
		if(ptr == NULL)
		{
			//使用默认80端口
			strcpy(ip, domain);
			strcpy(port, "80");
		}
		else
		{
			//使用url中端口
			*ptr = 0;
			strcpy(ip, domain);
			strcpy(port, ptr + 1);
		}

		return 0;
	}
}

//发送请求
void http_download_data_send(HTTP_DOWNLOAD_DESC *download)
{
	char buf[1024] = {0};
	char range[128] = {0};
	int range_start = download->file_dl_size + download->pack_index * download->data_buf_size;
	int range_end = download->file_dl_size + (download->pack_index + 1) * download->data_buf_size - 1;

	download->pack_size = 0;
	download->pack_dl_size = 0;
	download->is_unpacking = 0;

	if(download->file_size > 0)
	{
		if(range_start >= download->file_size)
		{
			range_start = download->file_size - 1;
		}
		
		if(range_end >= download->file_size)
		{
			range_end = download->file_size - 1;
		}
	}
	else
	{
		range_start = 0;
		range_end = 0;
	}
	
	sprintf(range, "%d-%d", range_start, range_end);
	sprintf(buf, HTTP_GET_FILE, download->path, download->ip, range);
	//DEBUG("range=%s", range);
	http_send(download->channel, buf, strlen(buf));
}

//接收数据
int http_download_data_recv(HTTP_DOWNLOAD_DESC *download)
{
	if(download->channel <= 0)
	{
		//DEBUG("channel no set");
		return -1;
	}

	if(download->pack_size == 0)
	{
		return http_recv(download->channel, download->pack_buf + download->pack_dl_size, download->data_buf_size);
	}
	else
	{
		if(download->is_unpacking == 1)
		{
			return http_recv(download->channel, download->pack_buf, HTTP_DATA_BUF_SIZE);
		}
		else
		{
			return http_recv(download->channel, download->pack_buf + download->pack_dl_size, download->pack_size - download->pack_dl_size);
		}
	}
}

//解析HTTP数据，先处理HTTP头，判断数据合法性，再处理HTTP数据，保存到文件
int http_download_data_parse(HTTP_DOWNLOAD_DESC *download)
{
	char *ptr = NULL;
	char *end = NULL;
	char range[64] = {0};

	if(download->is_unpacking == 1)
	{
		write(download->file_fd, download->pack_buf, download->recv_size);

		if(download->pack_size == download->pack_dl_size)
		{
			DEBUG("download finish __LINE__=%d", __LINE__);
			fsync(download->file_fd);
			return 0;
		}

		//继续接收数据包
		return -1;
	}

	//DEBUG("pack_buf = %s", download->pack_buf);

	if(download->pack_size == 0)
	{
		download->pack_size = http_get_package_length(download->pack_buf);
		if(download->pack_size == 0)
		{
			return -2;
		}

		if(strstr(download->pack_buf, "Content-Range") == NULL)
		{
			download->is_unpacking = 1;
			end = strstr(download->pack_buf, "\r\n\r\n");
			if(end)
			{
				lseek(download->file_fd, 0, SEEK_SET);
				ftruncate(download->file_fd, 0);
				write(download->file_fd, end + 4, download->pack_dl_size - (int)(end + 4 - download->pack_buf));
			}

			if(download->pack_size == download->pack_dl_size)
			{
				DEBUG("download finish __LINE__=%d", __LINE__);
				fsync(download->file_fd);
				return 0;
			}

			//继续接收数据包
			return -3;
		}
	}

	if(download->pack_size != download->pack_dl_size)
	{
		//包未接收完成，继续接收
		return -4;
	}

	//DEBUG("pack_size %d pack_dl_size %d", download->pack_size, download->pack_dl_size);

	ptr = strstr(download->pack_buf, "404 Not Found");
	if(ptr)
	{
		//服务器文件已不存在
		DEBUG("file not found");
		remove(download->file_name);
		return 0;
	}
	
	ptr = strstr(download->pack_buf, "Requested Range Not Satisfiable");
	if(ptr)
	{
		//请求超出文件范围
		DEBUG("requested range out");
		return 0;
	}

	download->pack_size = 0;
	download->pack_dl_size = 0;

	ptr = strstr(download->pack_buf, "Content-Range");
	end = strstr(ptr, "\r\n");
	if(ptr && end)
	{
		if(download->file_size > 0)
		{
			download->pack_index++; 	//pack_index加1 请求下个包
		}
		
		strncpy(range, ptr, end - ptr);
		sscanf(range, "Content-Range: bytes %d-%d/%d", &download->data_start, &download->data_end, &download->file_size);
	}
	
	end = strstr(download->pack_buf, "\r\n\r\n");
	if(end)
	{		
		//保存HTTP数据到文件
		if(lseek(download->file_fd, download->data_start, SEEK_SET) < 0)
		{
			DEBUG("lseek error");
		}
		
		if(write(download->file_fd, end + 4, download->data_end +1 - download->data_start) < 0)
		{
			DEBUG("write error");
		}
		
		DEBUG("download_size=%d file_size=%d", download->data_end + 1, download->file_size);
	}
	else
	{
		DEBUG("data error2");
	}
	
	if(download->data_end + 1 == download->file_size)
	{
		DEBUG("download finish __LINE__=%d", __LINE__);
		fsync(download->file_fd);
		return 0;
	}

	if(strstr(download->pack_buf, "Connection: close\r\n"))
	{
		DEBUG("Connection close");
		return -5;
	}

	return 1;//继续请求下一个包
}

//解析url连接
HTTP_DOWNLOAD_DESC *http_download_init(char *url, char *save_path)
{
	HTTP_DOWNLOAD_DESC *download = calloc(1, sizeof(HTTP_DOWNLOAD_DESC));
	if(download == NULL)
	{
		return NULL;
	}

	strncpy(download->url, url, sizeof(download->url));
	
	//从url中获取文件名
	if(http_get_file_name(download->url, save_path, download->file_name) < 0)
	{
		goto error1;
	}

	 //从url中获取文件路径
	if(http_get_path(download->url, download->path) < 0)
	{
		goto error1;
	}

	if(http_get_ip_port(download->url, download->ip, download->port) < 0)
	{
		goto error1;
	}
		
	//创建文件保存
	download->file_fd = open(download->file_name, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
	if(download->file_fd < 0)
	{
		DEBUG("open %s error", download->file_name);
		goto error1;
	}

	//获取已经下载文件大小，断点续传
	download->file_dl_size = lseek(download->file_fd, 0, SEEK_END); 

	//HTTP用户数据大小，HTTP头部最大大小
	download->data_buf_size = HTTP_DATA_BUF_SIZE;
	download->head_buf_size = HTTP_HEAD_BUF_SIZE;
	download->pack_buf = calloc(1, download->data_buf_size + download->head_buf_size);
	if(download->pack_buf == NULL)
	{
		goto error1;
	}

	return download;

error1:
	if(download->file_fd) close(download->file_fd);
	free(download);
	return NULL;
}

//释放分配的资源
void http_download_exit(HTTP_DOWNLOAD_DESC *download)
{
	if(download)
	{
		download->file_dl_size = lseek(download->file_fd, 0, SEEK_END);
		if(download->file_dl_size == 0) remove(download->file_name);
		if(download->file_fd > 0) close(download->file_fd);
		if(download->pack_buf) free(download->pack_buf);
		free(download);
	}
}

//设置HTTP数据通道：有串口 有TCP
void http_download_data_channel_set(HTTP_DOWNLOAD_DESC *download, int channel)
{
	//DEBUG("channel=%d", channel);
	download->channel = channel;
}

static void tcp_socket_disconnect(int socket_fd)
{
	if(socket_fd > 0) close(socket_fd);
}

static int tcp_socket_connect(char *ip, char *port)
{
	int ret;
	struct addrinfo *res;
	struct addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL};
	struct sockaddr_in *sinp;
	const char *addr;
	char ip_addr[128] = {0};
	char dst[128] = {0};
	
	int sockfd = -1;
	struct sockaddr_in ser_addr;
	struct timeval tv = {3, 0};

	ret = getaddrinfo(ip, NULL, &hints, &res);
	if(ret == 0)
	{
		sinp = (struct sockaddr_in *)res->ai_addr;
		addr = (const char *)inet_ntop(AF_INET, &sinp->sin_addr, dst, INET_ADDRSTRLEN);
		strcpy(ip_addr, addr);
		DEBUG("%s ip_addr=%s", __FUNCTION__, ip_addr);
		freeaddrinfo(res);
	}
	else
	{
		DEBUG("getaddrinfo error %s, ret=%d, %s", ip, ret, gai_strerror(ret));
		return -1;
	}

	ser_addr.sin_family = AF_INET;
	ser_addr.sin_addr.s_addr = inet_addr(ip_addr);
	ser_addr.sin_port = htons(atoi(port));

	//设置超时
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
	setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

	if(connect(sockfd, (struct sockaddr*)&ser_addr, sizeof(struct sockaddr_in)) == 0)
	{
		return sockfd;
	}
	else
	{
		close(sockfd);
		return -1;
	}
}

//使用TCP数据通道，通过HTTP协议下载url文件，保存到save_path路径，如果save_path为空，保存到当前路径
int http_tcp_download(char *url, char *save_path)
{
	int ret = 0;
	int retry = 10;
	HTTP_DOWNLOAD_DESC *download;

	//初始化http_download_desc
	download = http_download_init(url, save_path);
	if(download == NULL)
	{
		return -1;
	}

	while(retry > 0)
	{
		//接收HTTP下发数据
		if((ret = http_download_data_recv(download)) > 0)
		{
			download->recv_size = ret;
			download->pack_dl_size += ret;
			//处理数据
			ret = http_download_data_parse(download);
			if(ret == 0)
			{
				//下载完成
				break;
			}
			else if(ret == 1)
			{
				//HTTP请求继续发送
				http_download_data_send(download);
			}
			else
			{
				//DEBUG("ret=%d", ret);
			}
		}
		else
		{
			//关闭TCP连接
			tcp_socket_disconnect(download->channel);

			//设置数据通道：TCP作为数据通道
			//DEBUG("%s %s", download->ip, download->port);
			ret = tcp_socket_connect(download->ip, download->port);
			if(ret > 0) {
				http_download_data_channel_set(download, ret);

				//HTTP请求重发
				http_download_data_send(download);
			}

			if(ret < 0 || download->is_unpacking)
			{
				retry--;
				DEBUG("retry=%d", retry);
			}
		}
	}

	//关闭TCP连接
	tcp_socket_disconnect(download->channel);

	//释放资源
	http_download_exit(download);

	DEBUG("http_tcp_download ret=%d", ret);
	return ret;
}
